package com.kefu.chat.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import com.kefu.basic.mapper.BasicTagMapper;
import com.kefu.basic.model.BasicTag;
import com.kefu.chat.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kefu.basic.mapper.BasicRptMapper;
import com.kefu.basic.model.BasicRpt;
import com.kefu.basic.model.BasicRptCol;
import com.kefu.basic.util.RedisLockUtil;
import com.kefu.chat.mapper.ChatSessionMapper;
import com.kefu.chat.util.ChatConstant;
import com.kefu.chat.vo.ChatEventMsg.AdminChatConnectE;
import com.kefu.chat.vo.ChatEventMsg.UserChatConnectE;
import com.kefu.chat.vo.ChatRptSession;
import com.kefu.chat.vo.ChatRptSessionStatVo;
import com.kefu.chat.vo.ChatSessionRptVo;
import com.kefu.chat.vo.ChatSessionSearchVo;
import com.kefu.chat.vo.ChatUserSessionSearchVo;
import com.kefu.common.context.KefuContext;
import com.kefu.common.service.AbstractKefuService;
import com.kefu.common.util.Assert;
import com.kefu.common.util.JacksonUtil;
import com.kefu.common.util.StreamUtil;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;

@Service
public class ChatSessionService  extends AbstractKefuService<ChatSession>  {
    @Resource
    private ChatSessionMapper chatSessionMapper;
    @Autowired
    private ChatCacheService chatCacheService;
    @Autowired
    ChatChannelService chatChannelService;
    @Autowired
    ChatSessionMsgService chatSessionMsgService;
    @Autowired
    RedisLockUtil redisLockUtil;
    @Resource
    private BasicTagMapper basicTagMapper;
    @Resource
    private BasicRptMapper basicRptMapper;
    @Resource
    private ChatSessionTagRelService chatSessionTagRelService;

     
    public boolean chatConnect(ChatUser user, ChatAdmin admin, int connectType) {
        logger.info("chatConnect_start:{},{},{}", user, admin, connectType);
        //用户转人工上锁，避免接入多个客服
        if (redisLockUtil.beLock("chatConnect:"+user.getUserId())) {
            logger.info("chatConnect_tooMany:{},{},{}", user, admin, connectType);
            return false;
        }
        logger.info("chatConnect:{},{},{}", user, admin, connectType);
        if (user.getStatus() == ChatConstant.user_status_robot) {
            chatCacheService.delRobotSet(user.getTenantId(), user.getSessionId());
        }
        if (user.getStatus() == ChatConstant.user_status_wait) {
            chatCacheService.delWaitList(user.getTenantId(), user.getUserId());
        }
        if (connectType == ChatConstant.connect_type_transfer) {
            user.setTransferMode(1);
        }
        user.setStatus(ChatConstant.user_status_online);
        user.setOnlineTime(System.currentTimeMillis());
        if (user.getStartTime() == 0) user.setStartTime(user.getOnlineTime());
        user.setAdminId(admin.getAdminId());
        user.setAdminName(admin.getAdminName());
        user.setAdminNick(admin.getAdminNick());
        chatCacheService.setUser(user);
        if (connectType == ChatConstant.connect_type_direct || connectType == ChatConstant.connect_type_wait || connectType == ChatConstant.connect_type_invite) {
            ChatChannel channel = chatChannelService.getFromId(user.getChannelId());
            if (channel.getAdminWelcomeMode() == 1 && StrUtil.isNotBlank(channel.getAdminWelcomeMsg())) {
                ChatSessionMsg msgLog = ChatSessionMsg.builder().id(IdUtil.simpleUUID()).userId(user.getUserId()).sessionId(user.getSessionId()).tenantId(user.getTenantId()).sender(admin.getAdminId()).senderName(admin.getAdminNick())
                        .senderType(ChatConstant.msg_sender_admin).receiveType(ChatConstant.msg_sender_user).receive(user.getUserId()).msgType(1).msgContent(channel.getAdminWelcomeMsg())
                        .msgTime(System.currentTimeMillis()).adminId(admin.getAdminId()).build();
                chatSessionMsgService.insertSessionMsg(msgLog);
            }
            if (connectType == ChatConstant.connect_type_wait || connectType == ChatConstant.connect_type_invite) {
                UserChatConnectE userMsg = BeanUtil.toBean(admin, UserChatConnectE.class);
                chatCacheService.sendEventMsg(user.getPollId(), userMsg);
            }
        }
        chatCacheService.addAdminUserSet(admin.getAdminId(), user.getUserId());
        AdminChatConnectE adminMsg = BeanUtil.toBean(user, AdminChatConnectE.class);
        adminMsg.setConnectType(connectType);
        chatCacheService.sendEventMsg(admin.getPollId(), adminMsg);
        ChatSession chatSession = BeanUtil.toBean(user, ChatSession.class);
        chatSession.setOnlineMode(1);
        insertOrUpdateSession(chatSession);
        logger.info("chatConnect_end:{},{},{},{}", user, admin, chatSession, connectType);
        return true;
    }

     
    public void insertOrUpdateSession(ChatSession chatSession) {
        logger.info("insertOrUpdateSession:{}", chatSession);
        if(StrUtil.isBlank(chatSession.getStartTimeStr())&&chatSession.getStartTime()!=null){
            chatSession.setStartTimeStr(DateUtil.date(chatSession.getStartTime()).toString());
        }
        saveOrUpdate(chatSession);
    }

    public Page<ChatSession> userSessionRecord(ChatUserSessionSearchVo vo){
    	return chatSessionMapper.selectPage(vo);
    }

    public Page<Map<String, Object>> getSession(ChatSessionRptVo search) {
        Assert.isNull(search.getStartTimeStart(), "开始时间不能为空！");
        if (StrUtil.isNotBlank(search.getOrderField())) {
            search.setOrderField(StrUtil.toUnderlineCase(search.getOrderField()));
        }
        String rptId = search.getRptId();
        BasicRpt rpt = basicRptMapper.selectByPrimaryKey(rptId);
        Assert.isNull(rpt, "自定义字段已被删除!");
        String rptFields = rpt.getRptFields();
        Assert.isEmpty(rptFields, "未设置显示字段");
        List<BasicRptCol> basicRptCols = JSONUtil.parseArray(rptFields).toList(BasicRptCol.class);
        Page<Map<String, Object>> page = new Page<>(search.getPageNo(),
                search.getPageSize(), search.getPageSize() < 100);
        String sql = basicRptCols.stream().map(BasicRptCol::getCodeFunction).collect(Collectors.joining(","));
        Assert.isEmpty(sql, "报表配置异常！");
        QueryWrapper<ChatSession> wrapper = search.builder();
        if (StrUtil.isNotBlank(search.getMsgContent())) {
            List<ChatSessionMsg> list = chatSessionMsgService.list(Wrappers.lambdaQuery(ChatSessionMsg.class)
                    .like(ChatSessionMsg::getMsgContent, search.getMsgContent()));
            if (CollUtil.isEmpty(list)) {
                return page;
            }
            wrapper.in("session_id", StreamUtil.stream(list).map(ChatSessionMsg::getSessionId).collect(Collectors.toList()));
        }
        page.setTotal(chatSessionMapper.selectCount(wrapper));
        wrapper.select(sql);
        wrapper.last("LIMIT " + search.getPageSize() + " OFFSET " + (search.getPageSize() * (search.getPageNo() - 1)));
        List<Map<String, Object>> list = chatSessionMapper.selectMaps(wrapper);
        page.setRecords(list);
        return page;
    }

    public Page<ChatSession> getHistoryUser(ChatSessionSearchVo vo) {
    	Page<ChatSession> page = new Page<>(vo.getPageNo(), vo.getPageSize(),false);
    	return chatSessionMapper.getHistoryUser(page, vo);
    }
    
     
    public List<ChatRptSession> selectGroupByDate(ChatRptSessionStatVo vo) {
    	QueryWrapper<ChatSession> queryWrapper=Wrappers.query();
//    	String resultDate="FROM_UNIXTIME(start_time/1000,'%Y-%m-%d')";
    	String resultDate="SUBSTRING(start_time_str,1,10)";
        boolean sameDay = DateUtil.isSameDay(new Date(vo.getResultDateStart()), new Date(vo.getResultDateEnd()));
        if(sameDay) {
//    		resultDate="FROM_UNIXTIME(start_time/1000,'%Y-%m-%d %H')";
    		resultDate="SUBSTRING(start_time_str,1,13)";
    	}
//    	long startTime=DateUtil.parse(vo.getResultDateStart(), "yyyy-MM-dd").getTime();
//    	long endTime=DateUtil.parse(vo.getResultDateEnd()+" 23:59:59", "yyyy-MM-dd HH:mm:ss").getTime();
    	queryWrapper.select(resultDate+"  AS resultDate",
					    	"COUNT(1) AS totalCount",
					    	"SUM(total_time) AS totalTime",
					    	"SUM(robot_mode) AS robotCount",
					    	"SUM(online_mode) AS onlineCount",
					    	"SUM(wait_mode) AS waitCount");
    	queryWrapper.ge("start_time", vo.getResultDateStart());
    	queryWrapper.le("start_time", vo.getResultDateEnd());
    	queryWrapper.eq("tenant_id", KefuContext.getTid());
    	
    	queryWrapper.groupBy(resultDate);
    	queryWrapper.orderByAsc(resultDate);
    	
    	queryWrapper.last(" limit 100");
        List<ChatRptSession> chatRptSessions = chatSessionMapper.staticsList(queryWrapper, ChatRptSession.class);
        List<ChatRptSession> list=chatSessionMapper.staticsList(queryWrapper, ChatRptSession.class,(rpt)->{
    		if(DateUtil.isSameDay(new Date(vo.getResultDateStart()), new Date(vo.getResultDateEnd()))) {
    			String date=rpt.getResultDate();
                if(StrUtil.isNotBlank(date)){
                    rpt.setResultDate(date.substring(0,10));
                    rpt.setResultHour(date.substring(11,13)+":00");
                }
    		}
    	});
    	
    	return list;
    }

    public List<Object[]> sessionRecordExport(ChatSessionRptVo search) {
        Assert.isNull(search.getStartTimeStart(), "未选择开始时间！");
        BasicRpt rpt = basicRptMapper.selectByPrimaryKey(search.getRptId());
        Assert.isNull(rpt, "自定义字段已被删除!");
        String rptFields = rpt.getRptFields();
        Assert.isEmpty(rptFields, "未设置显示字段");
        List<BasicRptCol> basicRptCols = JSONUtil.parseArray(rptFields).toList(BasicRptCol.class);
        logger.info("会话纪录自定义统计:{}, \n 查询字段:{}", JacksonUtil.toString(rpt), JacksonUtil.toString(basicRptCols));
        if (StrUtil.isNotBlank(search.getOrderField())) {
            search.setOrderField(StrUtil.toUnderlineCase(search.getOrderField()));
        }
        search.setPageNo(1);
        search.setPageSize(50000);
        Page<Map<String, Object>> page = getSession(search);
        List<Object[]> exportList = new ArrayList<>();
        Object[] title = new Object[basicRptCols.size()];
        for (int i = 0; i < basicRptCols.size(); i++) {
            title[i] = msg(basicRptCols.get(i).getCodeName());
        }
        exportList.add(title);
        for (Map<String, Object> i : page.getRecords()) {
            Object[] row = new Object[basicRptCols.size()];
            for (int j = 0; j < basicRptCols.size(); j++) {
                Object cell = i.get(basicRptCols.get(j).getCodeId());
                switch (basicRptCols.get(j).getCodeId()) {
                    case "wait_mode":
                    case "transfer_mode":
                    case "online_mode":
                    case "mark_mode":
                    case "wo_create_mode":
                    case "evaluate_admin_mode":
                    case "evaluate_admin_solve":
                    case "summary_mode":
                    case "summary_solve":
                    case "robot_mode":
                    case "evaluate_robot_mode":
                    case "evaluate_robot_solve":
                        cell = modeType(Convert.toInt(cell));
                        break;
                    case "total_time":
                    case "first_reply_duration":
                    case "avg_reply_duration":
                        cell = DateUtil.secondToTime(cell != null ? Convert.toLong(cell).intValue() : 0);
                        break;
                    case "channel_type":
                        cell = channelType(Convert.toInt(cell));
                        break;
                    case "out_type":
                        cell = outType(Convert.toInt(cell));
                        break;
                }
                row[j] = cell;
            }
            exportList.add(row);
        }
        return exportList;
    }

    String outType(Integer type) {
        //会话结束方式：0客户结束，1会话超时，2客服结束
        String result = "";
        if (type == null || type == 0) {
            result = msg("客户结束");
        } else if (type == 1) {
            result = msg("会话超时");
        } else if (type == 2 || type == 4) {
            result = msg("客服结束");
        }
        return result;
    }

    String channelType(Integer type) {
        //渠道类型： 1桌面网站，2移动网站， 3APP， 4微信， 5小程序， 6企业微信
        String result = "";
        if (type == null || type == 1) {
            result = msg("桌面网站");
        } else if (type == 2) {
            result = msg("移动网站");
        } else if (type == 3) {
            result = msg("APP");
        } else if (type == 4) {
            result = msg("微信");
        } else if (type == 5) {
            result = msg("小程序");
        } else if (type == 6) {
            result = msg("企业微信");
        }
        return result;
    }

    String modeType(Integer type) {
        String result = "";
        if (type == null || type == 0) {
            result = msg("否");
        } else if (type == 1) {
            result = msg("是");
        }
        return result;
    }

    public Page<ChatSession> getSessionList(ChatSessionSearchVo search) {
        Page<ChatSession> page = new Page<>(search.getPageNo(), search.getPageSize(), search.getPageSize() < 100);
        page = chatSessionMapper.selectPage(page, search.builder());
        if (CollUtil.isNotEmpty(page.getRecords())) {
            for (ChatSession session :
                    page.getRecords()) {
                List<ChatSessionTagRel> list = chatSessionTagRelService.list(Wrappers.lambdaQuery(ChatSessionTagRel.class)
                        .eq(ChatSessionTagRel::getSessionId, session.getSessionId()));
                if (CollUtil.isNotEmpty(list)) {
                    List<BasicTag> basicTags = basicTagMapper.selectList(Wrappers.lambdaQuery(BasicTag.class)
                            .in(BasicTag::getTagId, list.stream().map(ChatSessionTagRel::getTagId).collect(Collectors.toList())));
                    session.setBasicTag(basicTags);
                }
            }
        }
        return page;
    }
}
